#ifndef prs_FrmUseTemplate_H
#define prs_FrmUseTemplate_H

#include "prs_Func.h"
#include "prs_FrmFunc.h"
#include "prs_FrmRules.h"
#include "prs_FrmValue.h"
#include "prs_FrmValueRule.h"
#include "prs_FrmDefTemplate.h"
#include "prs_CalcExpr.h"
#include "frm_control_TPanel.h"

namespace prs {

///////////////////////////////////////////////////////////////////////////////
struct TNodeUseTemplate;
struct TNodeUseTemplateParam;
///////////////////////////////////////////////////////////////////////////////

struct TMessage_Base// : public TAttrNode
{
  //DECLARE_ClientNode()
  //
  //TMessage_Base() { 
  //  templateInst = NULL;
  //}
  //
  bool InTemplate(TNodeUseTemplate &V) {
    return SearchTemplate(V) != NULL;
  }
  //
  TNodeUseTemplate *SearchTemplate(
    TNodeUseTemplate &TemplateType
  );
  //
  TNodeUseTemplate *SearchTemplateById(
    TNodeUseTemplate   &TemplateType,
    const svl::TString &TemplateId
  );
  //
  TNodeUseTemplate *SearchTemplateById(
    const svl::TString &TemplateId
  );

private:  
  //TNodeUseTemplate *templateInst;
  svl::TArrayPtrFix<TNodeUseTemplate*,20> templates;
  friend TNodeUseTemplate;
};

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

struct TMessage_SetTemplateParam : public TMessage_Base
{
  //
  TMessage_SetTemplateParam() {
    control = NULL;
  }
  //
  frm::TGeometryRect GetGRect() {
    frm::TGeometryRect GRect;
    if (control) {
		  control->GetWindow().CalcGeometryRect( control, GRect ); 
    }
    return GRect;
  }

private:
  frm::control::TPanel *control;
  friend TNodeUseTemplateParam;
};

///////////////////////////////////////////////////////////////////////////////

struct TMessage_BeforeSetTemplateParam : public TMessage_SetTemplateParam
{
  TNodeUseTemplateParam *Sender;
  TFrmValue             *NewValue;
  //
  TMessage_BeforeSetTemplateParam() {
    Sender = NULL;
    NewValue = NULL;
  }
};

///////////////////////////////////////////////////////////////////////////////

struct TMessage_AfterSetTemplateParam : public TMessage_SetTemplateParam
{
  TNodeUseTemplateParam *Sender;
  //
  TMessage_AfterSetTemplateParam() {
    Sender = NULL;
  }
};

///////////////////////////////////////////////////////////////////////////////

struct TNodeUseTemplateParam : public TNodeFrm
{
  DECLARE_ClientNode()
  //
  TAttrNodeType<nodeType_UseTemplateParam> NodeType;
  TAttrId Id;
  TAttrPointerNodeOwner<attrName_DefValue,TFrmValue*> DefValue;
  TAttrNodeArray<attrName_LinkAttr,TNodeFrmLinkAttrArray> LinkAttr;
  TAttrPointerNodeOwner<attrName_Expression,TNodeExpr*> Expression;
  //
  TNodeUseTemplateParam() : NodeType(this),Id(this),DefValue(this),LinkAttr(this),Expression(this) {}
  //
  static TNodeUseTemplateParam *SearchNearParamByTypeFromChild(TAttrNode *FromChild);
  //
  TNodeUseTemplate *GetUseTemplate();
  //
  /*void Calc();*/
  //
  bool CheckParamId(const svl::TString &Id_) { return Id.GetValue().CompareIC(Id_);}
  void BindParam();
  void SetValue(
    TFrmValue &Value,
    frm::control::TPanel *Control = NULL
  );
  //
  TAttrNode *GetBindControl();
  //
  TFrmControlsValue *TNodeUseTemplateParam::GetControlsValue();
  //
  bool CheckCompatibleValueType(
    TNodeDefTemplateParam *DefParam,
    svl::TString &OutError
  );
  //
  bool AddLinkParam(
    TNodeDefTemplateParam *DefParam,
    svl::TString &OutError
  );
  
  //
  template<class AttrType> static void setValueFromBindAttr(
    AttrType &Attr,
    frm::control::TPanel *Control
  ){
    TNodeUseTemplateParam *Param = Attr.GetBindNodePtr();
    if (!Param) {
      return;
    }

    static svl::TLocksStack::TState St;
    if (svl::TLocksStack::IsTop(St,Param)) { 
      return;
    }
    svl::TLocksStack Push(St,Param);
    
    TFrmValue *ParamValue = Param->DefValue;
    ParamValue->SetValueFromAttr(&Attr);

    Param->SetValue(*ParamValue,Control);
  }

private:  
  static void bindParamInParent(
    TAttrNode *InParent,
    TNodeUseTemplateParam *UseParam
  );
  static void setValueInParent(
    TAttrNode *InParent,
    TFrmValue *Value
  );
  static void getBindControlInParent(
    TAttrNode *InParent,
    TAttrNode *&OutControl
  );
  static TFrmControlsValue *getControlsValueInParent(
    TAttrNode *InParent
  );
  svl_Except;
};

///////////////////////////////////////////////////////////////////////////////

struct TNodeUseTemplateParams : public TNodeArrayT<TNodeUseTemplateParam>
{
};

///////////////////////////////////////////////////////////////////////////////

struct TNodeUseTemplateBody : public TNodeFrm
{
  DECLARE_ClientNode()
  //
  TAttrNodeType<nodeType_UseTemplateBody> NodeType;
  //
  TNodeUseTemplateBody() : NodeType(this) {}
};

///////////////////////////////////////////////////////////////////////////////

struct TNodeUseTemplate : public TNodeFrm
{
  DECLARE_ClientNode()
  //
  TAttrNodeType<nodeType_UseTemplate> NodeType;
  TAttrId Id;
  TAttrPointer<attrName_NodeDefTemplatePtr,TNodeDefTemplate*> NodeDefTemplatePtr;
  TAttrString<attrName_NodeDefTemplateId> NodeDefTemplateId;
//  TAttrPointerNoCopy<attrName_Sender,TNodeUseTemplate*> Sender;
  TAttrPointer/*NoCopy*/<attrName_Receiver,IBaseReceiver*> Receiver;
  TAttrNodeArray<attrName_Params,TNodeUseTemplateParams> Params;
  TAttrNodeObject<attrName_Body,TNodeUseTemplateBody> Body;
  //
  TNodeUseTemplate()
    :NodeType(this),Id(this),NodeDefTemplatePtr(this),NodeDefTemplateId(this)
    /*,Sender(this)*/,Receiver(this),Params(this),Body(this) 
  {}
  //
  TNodeUseTemplate *NewCopy() { return TNode::newCopyFrom(this);}
  //
  TNodeUseTemplateParam *SearchParamById(char *Id);
  void SetParamsValue();
  //
  TNodeUseTemplate *SearchUseTemplateByDefTemplateId(const svl::TString &DefTemplateId);
  //
  template<class T> static void sendMessage(T &Message) {
    TNodeSearch Search(Message.Sender); Search
     .SearchNearParentByType(TFrmDict::nodeType_UseTemplate,true)
    ;

    IBaseReceiver *Receiver = NULL;

    for (int N = 0; N < Search.Count(); N++) {
      TNodeUseTemplate *UseTemplate = dynamic_cast<TNodeUseTemplate*>(&Search.Get(N));
      Message.templates.AddLast(UseTemplate);

      //if (UseTemplate->Sender.GetValue()) {
      //  Message.templateInst = UseTemplate->Sender;
      //}
      if (UseTemplate->Receiver.GetValue()) {
        Receiver = UseTemplate->Receiver;
      }
    }

    if (Receiver) {
      SendMessage(Message,Receiver);
    }
  }
};

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

struct TRuleUseTemplate : public snt::TRule
{
  TRuleUseTemplate(
    snt::text::TContext &Context,
    svl::TVariant &VParentNode,
    TRuleFrmChildrenBase &RFrmChildren,
    TRuleFrmValue &RFrmValue
  );
  
private:
  TNodeUseTemplate *GetNodeUseTemplate() { return dynamic_cast<TNodeUseTemplate*>(vParentNode.Pointer());}
  //
  bool FUseTemplate     (snt::text::TIdent::TFuncClosureDoEntryParams &Params);
  bool FUseTemplateParam(snt::text::TIdent::TFuncClosureDoEntryParams &Params);
  bool FSetId           (snt::text::TIdent::TFuncClosureDoEntryParams &Params);
  //
  void FSetBody(snt::TParams &Params) { vParentNode = &GetNodeUseTemplate()->Body.Value;}
  //
  bool FAfterNewValue  (TRuleFrmValue::TFuncAfterNewValueParams &Params);
  bool FSetValueAsIdent(TRuleFrmValue::TFuncSetValueAsIdentParams &Params);
  //
  svl::TVariant &vParentNode;
  snt::TRule  RParams,RParam;
  snt::TAlter AParams;

  TRuleExprParam RExprParam;
  TRuleExpr rex;

  svl_Except;
};

///////////////////////////////////////////////////////////////////////////////
}

#endif
